package com.example.hotfix;

/*
 * author: caojunhao
 * date  : 2021/1/5
 * desc  : 热修复执行流程
 *
 * 1.获取到当前应用的PathClassLoader
 * 2.反射获取到BaseDexClassLoader类中的DexPathList的属性对象pathList--private final DexPathList pathList;
 * 3.反射修改pathList的dexElements
 *   3.1把补丁包patch.dex转换成Element[]
 *   3.2获得DexPathList类中的Element[]属性对象dexElements（也就是old的数据）-private Element[] dexElements;
 *   3.3 patch+old合并，并反射赋值给pathList的dexElements
 *
 *
 *
 */

import android.app.Application;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class Hotfix {

    public static void installPatch(Application application,File patchFile){

        try {
            //1.获取到当前应用的PathClassLoader
            ClassLoader classLoader = application.getClassLoader();

            //2.反射获取到DexPathList的属性对象pathList
            Field pathListField = ShareReflectUtil.findField(classLoader, "pathList");
            //真正的对象pathList
            Object pathList = pathListField.get(classLoader);

            //3.反射修改pathList的dexElements
            //3.1把补丁包换patch.dex转成Element[]
            List<File> files=new ArrayList<>();
            files.add(patchFile);

            File dexOutputDir=application.getCacheDir();

            ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();

            //也就是反射执行DexPathList类中的makePathElements方法，这个方法会帮我们把dex转换成Element[]
            //private static Element[] makePathElements(List<File> files, File optimizedDirectory,
            //            List<IOException> suppressedExceptions)
            Method makePathElements = ShareReflectUtil.findMethod(pathList, "makePathElements",List.class,File.class,List.class);
            Object[] patchElements= (Object[]) makePathElements.invoke(pathList,files,dexOutputDir,suppressedExceptions);

            //3.2获得pathList的dexElements的原先的数据（old）
            Field dexElementsField = ShareReflectUtil.findField(pathList, "dexElements");
            Object[] dexElements = (Object[]) dexElementsField.get(pathList);

            //3.3 patch+old合并，并反射赋值给pathList的dexElements
            Object[] newElements = (Object[]) Array.newInstance(patchElements.getClass().getComponentType(), patchElements.length + dexElements.length);
            //先把补丁包数组patchElements添加到新数组
            System.arraycopy(patchElements,0,newElements,0,patchElements.length);
            //接着在添加原先数据到新数组
            System.arraycopy(dexElements,0,newElements,patchElements.length,dexElements.length);
            //重新设置新数组给DexPathList类中的private Element[] dexElements;
            dexElementsField.set(pathList,newElements);
        } catch (Exception e) {
            //java.lang.IllegalArgumentException: method dalvik.system.DexPathList.makeDexElements argument 4 has type java.lang.ClassLoader, got dalvik.system.DexPathList
            e.printStackTrace();
        }

    }
}
